/**
 * Copyright 2005/6 Jeremy Nicholson
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package generator;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;

/**
 * a Java model of an OpenOffice.org Writer document The equivalent would be
 * available via the OO.o API but would involve firing up the application itself
 */
public class OpenOfficeDoc implements Cloneable {
	/**
	 * creates the bare minimum OO.o writer document clone a real document to
	 * get full formatting
	 */
	public OpenOfficeDoc() {
		setMimetype("application/vnd.sun.xml.writer"); // OO.o 1.1.5 doc
//		setMimetype("application/vnd.oasis.opendocument.text"); // OO.o 2.0 doc
	}
	
	public void setVersion(String v) {
		version = v;
		if (v.startsWith("1")) {
			setMimetype("application/vnd.sun.xml.writer");
		} else if (v.startsWith("2")) {
			setMimetype("application/vnd.oasis.opendocument.text");
		} else if (v.startsWith("3")) {
			setMimetype("application/vnd.oasis.opendocument.text");
		}
	}

	/**
	 * get the mimetype file
	 *
	 * @return the mimetype file
	 */
	public String getMimetype() {
		return (String) files.get(MIMETYPE);
	}

	public void setMimetype(Object s) {
		files.remove(MIMETYPE);
		if ("application/vnd.oasis.opendocument.text-template".equalsIgnoreCase(s.toString())){
			s="application/vnd.oasis.opendocument.text";
		}
		if (null != s) {
			files.put(MIMETYPE, s);
		}
	}

	public String getLayoutCache() {
		return (String) files.get(LAYOUTCACHE);
	}

	public void setLayoutCache(String s) {
		files.remove(LAYOUTCACHE);
		if (null != s) {
			files.put(LAYOUTCACHE, s);
		}
	}

	/**
	 * get the content or populate with the minimum empty document
	 *
	 * @return the content
	 */
	public String getContent() {
		String value = (String) files.get(CONTENT);
		if (null == value) {
			value = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
					+ "<office:document-content "
					+ "xmlns:office=\"http://openoffice.org/2000/office\" "
					+ "xmlns:style=\"http://openoffice.org/2000/style\" "
					+ "xmlns:text=\"http://openoffice.org/2000/text\" "
					+ "xmlns:table=\"http://openoffice.org/2000/table\" "
					+ "xmlns:draw=\"http://openoffice.org/2000/drawing\" "
					+ "xmlns:fo=\"http://www.w3.org/1999/XSL/Format\" "
					+ "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
					+ "xmlns:number=\"http://openoffice.org/2000/datastyle\" "
					+ "xmlns:svg=\"http://www.w3.org/2000/svg\" "
					+ "xmlns:chart=\"http://openoffice.org/2000/chart\" "
					+ "xmlns:dr3d=\"http://openoffice.org/2000/dr3d\" "
					+ "xmlns:math=\"http://www.w3.org/1998/Math/MathML\" "
					+ "xmlns:form=\"http://openoffice.org/2000/form\" "
					+ "xmlns:script=\"http://openoffice.org/2000/script\" "
					+ "xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" " // added
					// by
					// JN
					+ "office:class=\"text\" " + "office:version=\"1.0\" "
					+ ">" + "<office:script/>" + "<office:body>"
					+ "</office:body>" + "</office:document-content>";

			// TODO different header for OO.o 2.0
			// value = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
			// + "<office:document-content "
			// + "xmlns:office=\"http://openoffice.org/2000/office\" "
			// + "xmlns:style=\"http://openoffice.org/2000/style\" "
			// + "xmlns:text=\"http://openoffice.org/2000/text\" "
			// + "xmlns:table=\"http://openoffice.org/2000/table\" "
			// + "xmlns:draw=\"http://openoffice.org/2000/drawing\" "
			// + "xmlns:fo=\"http://www.w3.org/1999/XSL/Format\" "
			// + "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
			// + "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
			// + "xmlns:meta=\"http://openoffice.org/2000/meta\" "
			// + "xmlns:number=\"http://openoffice.org/2000/datastyle\" "
			// + "xmlns:svg=\"http://www.w3.org/2000/svg\" "
			// + "xmlns:chart=\"http://openoffice.org/2000/chart\" "
			// + "xmlns:dr3d=\"http://openoffice.org/2000/dr3d\" "
			// + "xmlns:math=\"http://www.w3.org/1998/Math/MathML\" "
			// + "xmlns:form=\"http://openoffice.org/2000/form\" "
			// + "xmlns:script=\"http://openoffice.org/2000/script\" "
			// + "xmlns:ooo=\"http://openoffice.org/2004/office\" "
			// + "xmlns:ooow=\"http://openoffice.org/2004/writer\" "
			// + "xmlns:oooc=\"http://openoffice.org/2004/calc\" "
			// + "xmlns:dom=\"http://www.w3.org/2001/xml-events\" "
			// + "xmlns:xforms=\"http://www.w3.org/2002/xforms\" "
			// + "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
			// + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
			// + "office:version=\"1.0\" "
			// + "office:class=\"text\">";

			value = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
				+ "office:document-content "
				+ "xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\""
				+ "xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\""
				+ "xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\""
				+ "xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\""
				+ "xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\""
				+ "xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\""
				+ "xmlns:xlink=\"http://www.w3.org/1999/xlink\""
				+ "xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
				+ "xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\""
				+ "xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\""
				+ "xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\""
				+ "xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\""
				+ "xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\""
				+ "xmlns:math=\"http://www.w3.org/1998/Math/MathML\""
				+ "xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\""
				+ "xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\""
				+ "xmlns:ooo=\"http://openoffice.org/2004/office\""
				+ "xmlns:ooow=\"http://openoffice.org/2004/writer\""
				+ "xmlns:oooc=\"http://openoffice.org/2004/calc\""
				+ "xmlns:dom=\"http://www.w3.org/2001/xml-events\""
				+ "xmlns:xforms=\"http://www.w3.org/2002/xforms\""
				+ "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
				+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
				+ "xmlns:rpt=\"http://openoffice.org/2005/report\""
				+ "xmlns:of=\"urn:oasis:names:tc:opendocument:xmlns:of:1.2\""
				+ "xmlns:rdfa=\"http://docs.oasis-open.org/opendocument/meta/rdfa#\""
				+ "office:version=\"1.2\">"
				+ "<office:scripts/>"
				+ "<office:font-face-decls><style:font-face style:name=\"Tahoma1\" svg:font-family=\"Tahoma\"/>"
				+ "<style:font-face style:name=\"Times New Roman\" svg:font-family=\"&apos;Times New Roman&apos;\" style:font-family-generic=\"roman\" style:font-pitch=\"variable\"/>"
				+ "<style:font-face style:name=\"Arial\" svg:font-family=\"Arial\" style:font-family-generic=\"swiss\" style:font-pitch=\"variable\"/>"
				+ "<style:font-face style:name=\"Arial Unicode MS\" svg:font-family=\"&apos;Arial Unicode MS&apos;\" style:font-family-generic=\"system\" style:font-pitch=\"variable\"/>"
				+ "<style:font-face style:name=\"Tahoma\" svg:font-family=\"Tahoma\" style:font-family-generic=\"system\" style:font-pitch=\"variable\"/>"
				+ "</office:font-face-decls>"
				+ "<office:automatic-styles/>"
				+ "<office:body>"
				+ "<office:text>"
				+ "</office:text>"
				+ "</office:body>"
				+ "</office:document-content>";

			setContent(value);
		}
		int dtdIndex = value.indexOf("office.dtd");
		if (dtdIndex > 0) {
			value = value.substring(dtdIndex + 12, value.length());
		}
		return value;
	}

	public void setContent(String s) {
		files.remove(CONTENT);
		if (null != s) {
			s = replaceBRForLineBreak(s);
			files.put(CONTENT, s);
			if (log.isDebugEnabled()) {
				log.debug("set content.xml");
			}
		}
		document = null;
	}
	
	/**
	 * get the document styles
	 *
	 * @return the styles
	 */
	public String getStyles() {
		return (String) files.get(STYLES);
	}

	public void setStyles(String s) {
		files.remove(STYLES);
		if (null != s) {
			files.put(STYLES, s);
			if (log.isDebugEnabled()) {
				log.debug("set styles.xml to:");
				log.debug(s.substring(0, 50) + "...");
			}
		}
	}

	/**
	 * meta.xml holds document summary data and needs to be reset or blanked
	 *
	 * @return
	 */
	public String getMeta() {
		return (String) files.get(META);
	}

	public void setMeta(String s) {
		files.remove(META);
		if (null != s) {
			files.put(META, s);
		}
	}

	public String getSettings() {
		return (String) files.get(SETTINGS);
	}

	public void setSettings(String s) {
		files.remove(SETTINGS);
		if (null != s) {
			files.put(SETTINGS, s);
		}
	}

	public String getManifest() {
		return (String) files.get(MANIFEST);
	}

	public void setManifest(String s) {
		files.remove(MANIFEST);
		if (null != s) {
			files.put(MANIFEST, s);
		}
	}

	/**
	 * add a file to the document recognised files are passed into special
	 * methods for unique processing
	 *
	 * @param id
	 * @param obj
	 */
	public void add(Object key, Object value) {
		String keyStr = (String) key; // assume that the key is a String
		if (keyStr.startsWith("Pictures/")) {
			addPicture(key, value);
			return;
		}
		if (log.isDebugEnabled()) {
			log.debug("add: " + key);
		}
		files.put(key, value);
	}

	public void removePicture(Object key) {
		if (log.isDebugEnabled()) {
			log.debug("remove picture: " + key);
		}
		Object removed = templatePictures.remove("Pictures/" + key);
		if (log.isDebugEnabled() && null != removed) {
			log.debug("removed " + key + " from template pictures");
		}
		removed = null;
		removed = pictures.remove(key);
		if (log.isDebugEnabled() && null != removed) {
			log.debug("removed " + key + " from document pictures");
		}
	}

	public void addPicture(Object key, Object value) {
		String keyStr = (String) key; // assume that the key is a String
		if (!keyStr.startsWith("Pictures/")) {
			keyStr = "Pictures/" + keyStr;
			if (log.isDebugEnabled()) {
				log.debug("add picture: " + keyStr);
			}
			pictures.put(keyStr, value);
			return;
		}
		if (log.isDebugEnabled()) {
			log.debug("add template picture: " + key);
		}
		if (null != key && null != value) {
			templatePictures.put(key, value);
		}
	}

	public Hashtable getFiles() {
		return files;
	}

	/**
	 * list all the pictures from the template and unique to this document
	 *
	 * @return
	 */
	public Hashtable getPictures() {
		Hashtable allPictures = new Hashtable();
		allPictures.putAll(templatePictures);
		allPictures.putAll(pictures);
		return allPictures;
	}

	/**
	 * create an empty document from this template. The pictures are static, so
	 * will be automatically shared by all current instances of this class
	 *
	 * @see java.lang.Object#clone()
	 */
	protected Object clone() throws CloneNotSupportedException {
		OpenOfficeDoc doc = new OpenOfficeDoc();
		// doc.setContent(this.getContent()); going to replace this with new
		// stuff
		// doc.setLayoutCache(this.getLayoutCache()); OO.o will regenerate on
		// first opening
		doc.setManifest(this.getManifest()); // or regenerate?
		doc.setMeta(this.getMeta());
		doc.setMimetype(this.getMimetype());
		doc.setSettings(this.getSettings());
		doc.setStyles(this.getStyles());
		return doc;
	}

	/*
	 * @see java.lang.Object#equals(java.lang.Object) TODO equals
	 */
	public boolean equals(Object obj) {
		return super.equals(obj);
	}

	/*
	 * @see java.lang.Object#hashCode() TODO hashcode
	 */
	public int hashCode() {
		return super.hashCode();
	}

	/*
	 * @see java.lang.Object#toString() TODO toString
	 */
	public String toString() {
		return super.toString();
	}

	public String getFileExtension() {
		if ("application/vnd.sun.xml.writer".equals(getMimetype())) {
			return "sxw";
		} else {
			return "odt";
		}
	}

	public List getImages() throws DocumentException {
		List list = getDocument().selectNodes("//draw:image");
		if (log.isDebugEnabled()) {
			log.debug(list.size() + " images");
		}
		Iterator it = list.iterator();
		List objects = new ArrayList();
		while (it.hasNext()) {
			Element element = (Element) it.next();
			OOImage image = new OOImage(element);
			if (log.isDebugEnabled()) {
				log.debug("image " + image);
			}
			objects.add(image);
		}
		return objects;
	}

	/*
	 * public List getTaggedImages() throws DocumentException { List list =
	 * getDocument().selectNodes("//draw:image[svg:desc]"); return list; }
	 */

	public List getTaggedImages() throws DocumentException {
		List list = getDocument().selectNodes("//draw:image");
		if (log.isDebugEnabled()) {
			log.debug(list.size() + " images");
		}
		Iterator it = list.iterator();
		List objects = new ArrayList();
		while (it.hasNext()) {
			Element element = (Element) it.next();
			OOImage image = new OOImage(element);
			if (image.isTagged()) {
				if (log.isDebugEnabled()) {
					log.debug("tagged image " + image);
				}
				objects.add(image);
			}
		}
		return objects;
	}

	/**
	 * get the content as a dom4j xml document so that it can be manipulated
	 *
	 * @return
	 * @throws DocumentException
	 */
	public Document getDocument() throws DocumentException {
		if (null == document) {
			document = OpenOfficeDocManager.getContentDocument(this);
		}
		return document;
	}

	/**
	 * set the stored content from the xml document
	 *
	 * @throws DocumentException
	 */
	public void setContent() throws DocumentException {
		Document xml = getDocument();
		setContent(xml.asXML());
	}

    public static String replaceBRForLineBreak(String xml) {
        if (log.isDebugEnabled() && xml.indexOf("<br/>") > 0) {
            log.debug("replacing br's");
        }
        xml = xml
                .replaceAll("<br/>",
                        "<text:line-break xmlns:text=\"http://openoffice.org/2000/text\"/>");
        return xml;
    }

	private Document document;

	private Hashtable files = new Hashtable();

	private static final String MIMETYPE = "mimetype";

	private static final String LAYOUTCACHE = "layout-cache";

	private static final String CONTENT = "content.xml";

	private static final String STYLES = "styles.xml";

	private static final String META = "meta.xml";

	private static final String SETTINGS = "settings.xml";

	private static final String MANIFEST = "META-INF/manifest.xml";

	/**
	 * the collection of template pictures is shared by every subsequent
	 * document *
	 */
	private static Hashtable templatePictures = new Hashtable();

	/** the collection of pictures is unique to the individual document * */
	private Hashtable pictures = new Hashtable();
	
	private String version = "1.1.5";

	private final static Log log = LogFactory.getLog(OpenOfficeDoc.class);

}
